package main

import (
	"bufio"
	"fmt"
	"io/ioutil"
	"log"
	"math"
	"os"
	"path"
	"regexp"
	"strconv"
	"strings"
	"sync"
	"syscall"
	"time"

	"github.com/faiface/beep"
	"github.com/faiface/beep/effects"
	"github.com/faiface/beep/mp3"
	"github.com/faiface/beep/speaker"
	cmap "github.com/orcaman/concurrent-map/v2"
)

//扫描目录
var filepath string = "/home/edison/音乐/"

// //绘制行
// func drawTextLine(screen tcell.Screen, x, y int, s string, style tcell.Style) {
// 	for _, r := range s {
// 		screen.SetContent(x, y, r, nil, style)
// 		if len(string(r)) == 3 || len(string(r)) == 2 {
// 			x = x + 2
// 		} else {
// 			x++
// 		}
// 	}
// }

var nowdispindex int = 0              //当前指向的歌曲
var nowdispmaxindex int = 0           //当前页最大歌曲数量
var nowpage int = 0                   //正在播放列表哪一页
var listarray = make([]listrow, 0)    //歌曲列表
var listLrcArray = make([]listrow, 0) //歌词列表
var tip string = ""                   //提示

// var lrcText = make([]byte, 0)
var lrcTextArr = make([]string, 0)

var nowplayindex = -1     //当前播放歌曲位于本页的下标
var nowplayarrindex = -1  //当前播放歌曲在数组中的下标
var nowplayindexpage = -1 //当前播放歌曲属于那个页面
var nowplaytitle = ""     //当前播放歌曲标题

var maxpage = 0     //最大页面
var maxpagerow = 10 //每页最大

// Create a new map.
// 创建一个map
var mapStr = cmap.New[string]()

//音频引擎
var ap *audioPanel
var fsteam *os.File
var ssc beep.StreamSeekCloser
var bformat beep.Format

type listrow struct {
	name     string
	filename string
}

type audioPanel struct {
	sampleRate beep.SampleRate
	streamer   beep.StreamSeeker
	ctrl       *beep.Ctrl
	resampler  *beep.Resampler
	volume     *effects.Volume
}

// 有名管道
var pipeFile = "/tmp/myplay.ipc"
var LrcPipeFile = "/tmp/myplayLrc.ipc"

// 互斥锁
var countGuard sync.Mutex

//
var speakerINit = false

// 播放完成
var done = make(chan bool)

//音频面板
func newAudioPanel(sampleRate beep.SampleRate, streamer beep.StreamSeeker) *audioPanel {
	// ctrl := &beep.Ctrl{Streamer: beep.Loop(-1, streamer)}

	ctrl := &beep.Ctrl{Streamer: beep.Seq(streamer, beep.Callback(func() {
		done <- true
	})), Paused: false}

	resampler := beep.ResampleRatio(4, 1, ctrl)
	volume := &effects.Volume{Streamer: resampler, Base: 2}
	return &audioPanel{sampleRate, streamer, ctrl, resampler, volume}
}

func (ap *audioPanel) play() {
	speaker.Play(ap.volume)
}

// //画面绘制
// func TDrawCls(screen tcell.Screen) {
// 	//主页背景样式
// 	mainBackgroundStyle := tcell.StyleDefault.
// 		Foreground(tcell.NewHexColor(0xD7D8A2))

// 	//主页样式
// 	mainStyle := tcell.StyleDefault.
// 		Foreground(tcell.ColorGreen)

// 	//播放样式
// 	mainPlayStyle := tcell.StyleDefault.
// 		Foreground(tcell.ColorRed)

// 	//选择样式
// 	selectStyle := tcell.StyleDefault.
// 		Background(tcell.ColorWhiteSmoke).
// 		Foreground(tcell.ColorGreen)

// 	//选择并正在播放样式
// 	PlaySelectStyle := tcell.StyleDefault.
// 		Background(tcell.ColorWhiteSmoke).
// 		Foreground(tcell.ColorRed)

// 	//提示样式
// 	tipStyle := tcell.StyleDefault.
// 		Foreground(tcell.ColorBlue)

// 	//清空
// 	screen.Fill(' ', mainBackgroundStyle)

// 	//绘制列表
// 	startv := nowpage * maxpagerow
// 	lenv := 0
// 	if nowpage == maxpage {
// 		lenv = len(listarray) - startv
// 	} else {
// 		lenv = (nowpage+1)*maxpagerow - startv
// 	}

// 	nowdispmaxindex = lenv
// 	countt := 0                                                                                 //指定绘制行数
// 	drawTextLine(screen, 5, 1, fmt.Sprintf("%d/%d", nowpage+1, maxpage+1), mainBackgroundStyle) //当前显示页数/最大页数
// 	for i := 0; i < lenv; i++ {
// 		//判断是否是当前光标指向的项目
// 		if countt == nowdispindex {
// 			//判断是否是正在播放的
// 			if nowplayindexpage == nowpage && countt == nowplayindex {
// 				drawTextLine(screen, 0, 3+countt, " >>> "+listarray[startv+countt].name, PlaySelectStyle)
// 			} else {
// 				drawTextLine(screen, 5, 3+countt, listarray[startv+countt].name, selectStyle)
// 			}
// 		} else {
// 			//未选择项目
// 			if nowplayindexpage == nowpage && countt == nowplayindex {
// 				drawTextLine(screen, 0, 3+countt, " >>> "+listarray[startv+countt].name, mainPlayStyle)
// 			} else {
// 				drawTextLine(screen, 5, 3+countt, listarray[startv+countt].name, mainStyle)
// 			}
// 		}
// 		countt++
// 	}
// 	drawTextLine(screen, 5, 30, tip, tipStyle)          //绘制提示
// 	drawTextLine(screen, 5, 17, nowplaytitle, tipStyle) //绘制当前播放歌曲名称

// 	//处理与绘制播放时长
// 	var positionStatus string = ""
// 	if ap != nil {
// 		position := ap.sampleRate.D(ap.streamer.Position())
// 		length := ap.sampleRate.D(ap.streamer.Len())
// 		positionStatus = fmt.Sprintf("%v / %v", fmtDuration(position), fmtDuration(length))
// 	} else {
// 		positionStatus = "00:00:00 / 00:00:00"
// 	}
// 	drawTextLine(screen, 5, 18, positionStatus, mainBackgroundStyle)

// 	//绘制音量
// 	var volumeStatus string = ""
// 	if ap != nil {
// 		volume := ap.volume.Volume
// 		volumeStatus = fmt.Sprintf("音量: %.1f", volume)
// 	} else {
// 		volumeStatus = "音量: 0.0"
// 	}
// 	drawTextLine(screen, 5, 19, volumeStatus, mainBackgroundStyle)

// 	//绘制速度
// 	var speedStatus string = ""
// 	if ap != nil {
// 		speed := ap.resampler.Ratio()
// 		speedStatus = fmt.Sprintf("速度: %.3fx", speed)
// 	} else {
// 		speedStatus = "速度: 0.000x"
// 	}
// 	drawTextLine(screen, 5, 20, speedStatus, mainBackgroundStyle)

// 	//绘制播放状态
// 	var state string = "-"
// 	if ap != nil {
// 		if ap.ctrl.Paused {
// 			state = "暂停"
// 		} else {
// 			state = "播放"
// 		}
// 	} else {
// 		state = "停止"
// 	}
// 	drawTextLine(screen, 5, 21, "播放状态: "+state, mainBackgroundStyle)
// }

//按键事件响应
// func THandle(event tcell.Event) (change, exit bool) {
func THandle(cmd string) (change, exit bool) {
	// switch event := event.(type) {
	// case *tcell.EventKey:
	//处理退出
	// if event.Key() == tcell.KeyESC {
	// 	return false, true
	// }

	// if event.Key() == tcell.KeyEnter {

	// }

	// if event.Key() != tcell.KeyRune {
	// 	return false, false
	// }

	// //处理按键
	// switch unicode.ToLower(event.Rune()) {
	// case ' ':
	if cmd == "pause\n" {
		speaker.Lock()
		if ap != nil {
			ap.ctrl.Paused = !ap.ctrl.Paused
		}
		speaker.Unlock()
		return true, false
	}

	// case 'q', 'e':
	// 	//播放时间
	// 	speaker.Lock()
	// 	newPos := ap.streamer.Position()
	// 	if event.Rune() == 'q' {
	// 		newPos -= ap.sampleRate.N(time.Second * 5)
	// 	}
	// 	if event.Rune() == 'e' {
	// 		newPos += ap.sampleRate.N(time.Second * 5)
	// 	}
	// 	if newPos < 0 {
	// 		newPos = 0
	// 	}
	// 	if newPos >= ap.streamer.Len() {
	// 		newPos = ap.streamer.Len() - 1
	// 	}
	// 	if err := ap.streamer.Seek(newPos); err != nil {
	// 		report(err)
	// 	}
	// 	speaker.Unlock()
	// 	return true, false

	// case 'w':

	if cmd == "prev\n" {
		//上一曲
		if nowdispindex-1 > -1 {
			nowdispindex--
		} else {
			if nowpage-1 > -1 {
				nowpage--
				nowdispindex = maxpagerow - 1
			}
		}
	}

	// 	return true, false
	// case 's':

	if cmd == "next\n" {
		//下一曲
		if nowdispindex+1 < nowdispmaxindex {
			nowdispindex++
		} else {
			if nowpage+1 < maxpage+1 {
				nowpage++
				nowdispindex = 0
			}
		}
	}
	// 	return true, false

	//播放
	if cmd == "play\n" || cmd == "next\n" || cmd == "prev\n" {
		fmt.Println("playar")
		countGuard.Lock()
		if speakerINit {
			speaker.Lock()
			if ap != nil {
				ap.ctrl.Paused = !ap.ctrl.Paused
			}
			speaker.Unlock()
			speakerINit = false
			// 暂停并停留 1/5秒
			time.Sleep(time.Second / 5)

		}

		tip = string("播放")
		if ssc != nil {
			ssc.Close()
			ssc = nil
		}
		if fsteam != nil {
			fsteam.Close()
			fsteam = nil
		}

		var errs error
		nowplayindex = nowdispindex
		nowplayarrindex = nowpage*maxpagerow + nowdispindex

		// fsteam, errs = os.Open(filepath + "/" + listarray[1].filename)
		fsteam, errs = os.Open(filepath + "/" + listarray[nowplayarrindex].filename)
		if errs != nil {
			report(errs)
		}

		nowplayindexpage = nowpage

		var errs2 error
		ssc, bformat, errs2 = mp3.Decode(fsteam)
		if errs2 != nil {
			report(errs2)
		}
		fmt.Println(filepath + "/" + listarray[nowplayarrindex].filename)
		// 清空并停留1/2秒,确保完全清空后再init,否则可能会死锁init方法
		speaker.Clear()
		time.Sleep(time.Second / 2)
		speaker.Init(bformat.SampleRate, bformat.SampleRate.N(time.Second/100))
		// 已加载标识
		speakerINit = true
		ap = nil
		ap = newAudioPanel(bformat.SampleRate, ssc)
		// nowplaytitle = listarray[1].filename

		nowPlayName := listarray[nowplayarrindex].filename
		fileType := path.Ext(nowPlayName)
		nowplaytitle = strings.TrimSuffix(nowPlayName, fileType)
		ap.play()
		// fmt.Println("playar1")

		// 歌词加载
		for _, v := range listLrcArray {
			if v.name == nowplaytitle+".lrc" {

				file, err := os.OpenFile(filepath+"/"+v.name, os.O_RDONLY, 0)
				if err != nil {
					panic(err)
				}
				defer file.Close()
				lrcText, err := ioutil.ReadAll(file)
				lrcTextArr = strings.Split(string(lrcText), "\n")

				// fmt.Println(string(lrcText))

			}

			// fmt.Println(k, v.name)
		}

		mapStr.Set("nowplaytitle", nowplaytitle)
		mapStr.Set("lrc", "")

		// speaker.Play(beep.Seq(ap.volume, beep.Callback(func() {
		// 	done <- true
		// })))
		countGuard.Unlock()

		return true, false
	}

	// case 'a':
	// 	//减速播放
	// 	speaker.Lock()
	// 	if ap != nil {
	// 		ap.resampler.SetRatio(ap.resampler.Ratio() * 15 / 16)
	// 	}
	// 	speaker.Unlock()
	// 	return true, false

	// case 'd':
	// 	//加速播放
	// 	speaker.Lock()
	// 	if ap != nil {
	// 		ap.resampler.SetRatio(ap.resampler.Ratio() * 16 / 15)
	// 	}
	// 	speaker.Unlock()
	// 	return true, false
	// case 'r':

	if cmd == "+\n" {

		//增大音量
		speaker.Lock()
		if ap != nil {
			ap.volume.Volume += 1
		}
		speaker.Unlock()
		return true, false
	}
	// case 'f':

	if cmd == "-\n" {

		//减小音量
		speaker.Lock()
		if ap != nil {
			ap.volume.Volume -= 1
		}
		speaker.Unlock()
		return true, false
	}

	// 如果要求获取歌词,则写入
	if cmd == "getLrc\n" {
		// lrcWrite("LRC:这是歌词")
		lrc, ok := mapStr.Get("lrc")
		nowplaytitle, ok = mapStr.Get("nowplaytitle")

		if !ok {
			lrcWrite("" + "《" + nowplaytitle + "》")
		} else {
			lrcWrite(lrc + "《" + nowplaytitle + "》")
		}

	}

	// case 'b':
	// 	// 结束程序
	// 	os.Exit(0)
	// 	return true, false
	// default:
	// 	//未知键写在提示
	// 	tip = string(unicode.ToLower(event.Rune()))
	// 	return false, false
	// }
	// }
	return false, false
}

//主函数
func main() {

	args := os.Args
	argslen := len(args)
	if argslen == 0 {
		fmt.Println("请输入参数")
	}
	if args[1] == "start" {
		// 建立一个管道 传输命令
		os.Remove(pipeFile)
		err := syscall.Mkfifo(pipeFile, 0666)
		if err != nil {
			log.Fatal("create named pipe error:", err)
		}

		// 建立一个管道 传输歌词
		os.Remove(LrcPipeFile)
		err = syscall.Mkfifo(LrcPipeFile, 0666)
		if err != nil {
			log.Fatal("create named pipe error:", err)
		}

	} else if args[1] == "next" {
		//  下一曲

		write("next")
		os.Exit(0)
	} else if args[1] == "prev" {
		// 上一曲

		write("prev")
		os.Exit(0)
	} else if args[1] == "pause" {
		// 暂停
		write("pause")
		os.Exit(0)
	} else if args[1] == "play" {
		// 播放
		write("play")
		os.Exit(0)
	} else if args[1] == "+" {
		// +音量
		write("+")
		os.Exit(0)
	} else if args[1] == "-" {
		// -音量
		write("-")
		os.Exit(0)
	} else if args[1] == "getLrc" {
		// getLrc
		write("getLrc")

		// 写入管道,并获取管道内指定返回
		// fmt.Println("open a named pipe file for read.")
		file, _ := os.OpenFile(LrcPipeFile, os.O_RDWR, os.ModeNamedPipe)
		reader := bufio.NewReader(file)
		ch1 := make(chan int, 10)

		timer := time.NewTimer(time.Second / 2) // 设置定时器的超时时间，主线程只等0.5秒

		go func(ch chan<- int) {
			lineLrc, err := reader.ReadBytes('\n')
			// fmt.Println("read...")
			if err == nil {
				fmt.Print(string(lineLrc))
				os.Exit(0)
			}
			ch <- 1
		}(ch1)

		select {
		case <-ch1:
			fmt.Println("从channel 1 收到一个数字")
		case <-timer.C: // 定时器也是一个通道
			// fmt.Println("1秒到了，超时了，main协程不等了")
			fmt.Println("loding...")
			os.Exit(0)
		}
	} else {
		fmt.Println("无效参数!")
		os.Exit(0)
	}

	// for {
	// 	fmt.Println("11")
	// 	time.Sleep(1 * time.Second)
	// }

	//扫描指定目录
	fmt.Println("scan start")
	files, err := ioutil.ReadDir(filepath)
	if err != nil {
		log.Fatal(err)
	}

	filescount := 0
	for _, file := range files {
		fileType := path.Ext(file.Name())
		if !file.IsDir() && fileType == ".mp3" {
			listarray = append(listarray, listrow{name: file.Name(), filename: file.Name()})
			filescount++
		}

		if !file.IsDir() && fileType == ".lrc" {
			listLrcArray = append(listLrcArray, listrow{name: file.Name()})
		}
	}
	if len(listarray) < maxpagerow+1 {
		nowdispmaxindex = len(listarray)
	} else {
		nowdispmaxindex = maxpagerow
	}
	// fmt.Println(nowdispmaxindex)

	//最大页面
	maxpage = round((float64(len(listarray)) / float64(maxpagerow)))
	fmt.Println("scan ok")

	// startv := nowpage * maxpagerow
	// lenv := 0
	// if nowpage == maxpage {
	// 	lenv = len(listarray) - startv
	// } else {
	// 	lenv = (nowpage+1)*maxpagerow - startv
	// }

	// nowdispmaxindex = lenv

	// screen, err := tcell.NewScreen()
	// if err != nil {
	// 	report(err)
	// }
	// err = screen.Init()
	// if err != nil {
	// 	report(err)
	// }
	// defer screen.Fini()

	// 提前绘制一帧
	// screen.Clear()
	// TDrawCls(screen)
	// screen.Show()

	seconds := time.Tick(time.Second / 11)
	// events := make(chan tcell.Event)
	// go func() {
	// 	for {
	// 		events <- screen.PollEvent()
	// 	}
	// }()

	go read()

	for {
		select {
		// case event := <-events:
		// 	change, exit := THandle(event)
		// 	if exit {
		// 		break loop
		// 	}
		// 	if change {
		// 		//发生改变则重新绘制
		// 		screen.Clear()
		// 		TDrawCls(screen)
		// 		screen.Show()
		// 	}

		case <-done:
			// 播放完成 下一曲
			fmt.Println("播放完成,下一曲")
			change, exit := THandle("next\n")
			if exit {
				break
			}
			if change {

			}

		case <-seconds:

			if ap != nil {

				speaker.Lock()
				// fmt.Println(bformat.SampleRate.D(ap.streamer.Position()).Round(time.Microsecond))
				m, s, ms := fmtDuration(bformat.SampleRate.D(ap.streamer.Position()))

				speaker.Unlock()

				if nowplaytitle != "" {

					intM, _ := strconv.Atoi(m)
					intS, _ := strconv.Atoi(s)
					intMS, _ := strconv.Atoi(ms)

					for k, v := range lrcTextArr {
						// fmt.Println(k, string(v))
						rex := regexp.MustCompile(`\[(\d{2}):(\d{2}).(\d{2,3})\](.*)`)
						out := rex.FindAllStringSubmatch(v, -1)
						for _, i := range out {
							// fmt.Println(i[1], i[2], i[3])

							lrcIntM, _ := strconv.Atoi(i[1])
							lrcIntS, _ := strconv.Atoi(i[2])
							lrcIntMS, _ := strconv.Atoi(i[3])

							if intM > lrcIntM || (lrcIntM == intM && intS > lrcIntS) || (lrcIntM == intM && lrcIntS == intS && intMS > lrcIntMS) {
								mapStr.Set("lrc", i[4])
								// fmt.Println(lrcText)

								// 删除该行
								lrcTextArr = append(lrcTextArr[:k], lrcTextArr[k+1:]...)
							}
						}

					}

					// listLrcArray
					// for k, v := range listLrcArray {
					// 	if v.name == nowplaytitle+".lrc" {

					// 		file, err := os.OpenFile(filepath+"/"+v.name, os.O_RDONLY, 0)
					// 		if err != nil {
					// 			panic(err)
					// 		}
					// 		defer file.Close()
					// 		content, err := ioutil.ReadAll(file)
					// 		fmt.Println(string(content))

					// 		rex := regexp.MustCompile(`\[(\d{2}):(\d{2}).(\d{2,3})\](.*)`)
					// 		out := rex.FindAllStringSubmatch(string(content), -1)
					// 		for _, i := range out {
					// 			fmt.Println(i[1], i[2], i[3])

					// 			lrcIntM, _ := strconv.Atoi(i[1])
					// 			lrcIntS, _ := strconv.Atoi(i[2])
					// 			lrcIntMS, _ := strconv.Atoi(i[3])

					// 			if intM > lrcIntM || (lrcIntM == intM && intS > lrcIntS) || (lrcIntM == intM && lrcIntS == intS && intMS > lrcIntMS) {
					// 				mapStr.Set("lrc", i[4])
					// 			}

					// 		}

					// 	}

					// 	fmt.Println(k, v.name)
					// }
				}
			}

			bar, ok := mapStr.Get("cmd")
			if ok {
				mapStr.Remove("cmd")
			} else {
				continue
			}
			// fmt.Println(bar)
			// 读

			change, exit := THandle(bar)
			if exit {
				break
			}
			if change {
				//发生改变则重新绘制
				// screen.Clear()
				// TDrawCls(screen)
				// screen.Show()
			}
			// screen.Clear()
			// TDrawCls(screen)
			// screen.Show()

		}
	}
}

// 管道读
func read() string {
	// fmt.Println("open a named pipe file for read.")
	file, _ := os.OpenFile(pipeFile, os.O_RDWR, os.ModeNamedPipe)
	reader := bufio.NewReader(file)

	for {
		line, err := reader.ReadBytes('\n')
		// fmt.Println("read...")
		if err == nil {
			// fmt.Print("load string: " + string(line))
			mapStr.Set("cmd", string(line))
		}

	}
}

// 管道写
func write(postStr string) {
	// fmt.Println("start schedule writing.")
	f, err := os.OpenFile(pipeFile, os.O_RDWR, 0777)
	if err != nil {
		log.Fatalf("error opening file: %v", err)
	}
	// fmt.Println("write string to named pipe file.")
	f.WriteString(fmt.Sprintf(postStr + "\n"))

}

// 管道写歌词
func lrcWrite(postStr string) {
	// fmt.Println("start schedule writing.")
	f, err := os.OpenFile(
		LrcPipeFile, os.O_RDWR, 0777)
	if err != nil {
		log.Fatalf("error opening file: %v", err)
	}
	// fmt.Println("write string to named pipe file.")
	f.WriteString(fmt.Sprintf(postStr + "\n"))

}

//错误处理
func report(err error) {
	fmt.Fprintln(os.Stderr, err)
	os.Exit(1)
}

//处理小数点
func round(x float64) int {
	return int(math.Floor(x + 0.5))
}

//格式化时间
func fmtDuration(d time.Duration) (string, string, string) {
	d = d.Round(time.Millisecond)
	h := d / time.Hour
	d -= h * time.Hour
	m := d / time.Minute
	d -= m * time.Minute
	s := d / time.Second
	d -= s * time.Second
	ms := d / time.Millisecond / 10

	return fmt.Sprintf("%02d", m), fmt.Sprintf("%02d", s), fmt.Sprintf("%03d", ms)
	// return fmt.Sprintf("%02d:%02d.%02d", m, s, ms)
}
